aboutsummaryrefslogtreecommitdiff
path: root/src/routes/projects/[projectId]
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/projects/[projectId]')
-rw-r--r--src/routes/projects/[projectId]/routine/[routineId]/+page.svelte87
1 files changed, 8 insertions, 79 deletions
diff --git a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte
index bcdca79..ab2644d 100644
--- a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte
+++ b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte
@@ -1,5 +1,6 @@
<script lang="ts">
import { enhance } from '$app/forms';
+ import HealthChart from '$lib/components/HealthChart.svelte';
import ScoreBadge from '$lib/components/ScoreBadge.svelte';
import StatusBadge from '$lib/components/StatusBadge.svelte';
import {
@@ -7,21 +8,17 @@
parseIntervalToDays,
computeHealthSeries,
} from '$lib/shared/routine-metrics';
-
let { data, form } = $props();
-
let name = $state('');
let frequency = $state('');
let description = $state('');
let note = $state('');
let saved = $state(false);
-
$effect(() => {
name = data.routine.name;
frequency = `${data.routine.frequencyDays}d`;
description = data.routine.description;
});
-
$effect(() => {
if (form?.success && form?.type === 'edit') {
saved = true;
@@ -29,9 +26,7 @@
return () => clearTimeout(t);
}
});
-
const frequencyDays = $derived(parseIntervalToDays(frequency));
-
const dateFormatter = new Intl.DateTimeFormat('fr-FR', {
day: 'numeric',
month: 'short',
@@ -41,39 +36,15 @@
month: 'short',
year: 'numeric',
});
-
const frozenAt = $derived(
data.routine.archived
? Date.parse(data.routine.archivedAt ?? data.routine.createdAt)
: Date.now(),
);
-
const series = $derived(
computeHealthSeries(data.routine, data.completions, frozenAt),
);
-
- const PAD = 10;
- const W = 800;
- const H = 140;
- const geometry = $derived.by(() => {
- if (series.length < 2) return null;
- const minT = series[0].at;
- const maxT = series[series.length - 1].at;
- const span = Math.max(1, maxT - minT);
- const x = (t: number) => PAD + ((t - minT) / span) * (W - 2 * PAD);
- const y = (h: number) => H - PAD - (h / 100) * (H - 2 * PAD);
- const points = series
- .map((p) => `${x(p.at).toFixed(1)},${y(p.health).toFixed(1)}`)
- .join(' ');
- const grid = [0, 25, 50, 75, 100].map((h) => ({ y: y(h), label: h }));
- const markers = data.completions
- .map((c) => {
- const t = Date.parse(c.doneAt);
- return t >= minT && t <= maxT ? { x: x(t), y: y(100) } : null;
- })
- .filter((m): m is { x: number; y: number } => m !== null);
- return { points, grid, markers };
- });
+ const markers = $derived(data.completions.map((c) => Date.parse(c.doneAt)));
</script>
<main>
@@ -159,28 +130,12 @@
<h2 class="section-title">Santé</h2>
<div class="form-card chart-card">
- {#if geometry}
- <svg
- class="health-chart"
- viewBox="0 0 {W} {H}"
- preserveAspectRatio="none"
- role="img"
- aria-label="Évolution de la santé de la routine dans le temps"
- >
- {#each geometry.grid as line (line.label)}
- <line
- x1={PAD}
- y1={line.y}
- x2={W - PAD}
- y2={line.y}
- class="chart-grid"
- />
- {/each}
- <polyline points={geometry.points} class="chart-line" fill="none" />
- {#each geometry.markers as m (m.x)}
- <circle cx={m.x} cy={m.y} r="4" class="chart-marker" />
- {/each}
- </svg>
+ <HealthChart
+ {series}
+ {markers}
+ ariaLabel="Évolution de la santé de la routine dans le temps"
+ />
+ {#if series.length >= 2}
<div class="chart-footer">
<StatusBadge
status={data.state.status}
@@ -195,8 +150,6 @@
{/if}
</span>
</div>
- {:else}
- <p class="empty">Pas encore assez d'historique pour tracer la courbe.</p>
{/if}
</div>
@@ -280,30 +233,6 @@
align-items: stretch;
}
- .health-chart {
- width: 100%;
- height: 9rem;
- }
-
- .chart-grid {
- stroke: var(--border);
- stroke-width: 1;
- stroke-dasharray: 2 4;
- }
-
- .chart-line {
- stroke: var(--accent);
- stroke-width: 2;
- stroke-linejoin: round;
- stroke-linecap: round;
- }
-
- .chart-marker {
- fill: var(--success);
- stroke: var(--white);
- stroke-width: 1.5;
- }
-
.chart-footer {
display: flex;
align-items: center;